/**
 * Created by Administrator on 2017/11/1.
 * Copyright 2017-2018 汉威.智慧环保事业部 Limited.
 * All rights reserved.
 */
package com.hw.one.collect.radar.Listener;

import com.alibaba.fastjson.JSON;
import com.hw.one.collect.mapper.RadarBaseDao;
import com.hw.one.collect.radar.po.MultipleDataPo;
import com.hw.one.collect.radar.po.RadarSitePo;
import com.hw.one.collect.radar.po.SingleDataPo;
import com.hw.one.collect.radar.service.RadarService;
import com.hw.one.collect.radar.util.LocationUtil;
import com.hw.one.collect.radar.util.Rasterize;
import com.hw.one.collect.radar.util.RegexUtil;
import com.hw.one.collect.util.Base64Util;
import com.hw.one.collect.util.ConvertUtil;
import com.hw.one.collect.util.MessageProducer;
import com.hw.one.collect.util.ZLibUtils;
import com.rabbitmq.client.Channel;
import org.apache.log4j.Logger;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Component
public class MultipleRadarReceiver implements ChannelAwareMessageListener {

    private static Logger log = Logger.getLogger(MultipleRadarReceiver.class);
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static SimpleDateFormat sdfYMD = new SimpleDateFormat("yyyy-MM-dd");
    private static DecimalFormat df2 = new DecimalFormat("#0.00");
    @Resource
    private RadarService radarService;
    @Resource
    private MessageProducer messageProducer;
    @Resource
    private RadarBaseDao radarBaseDao;

    /**
     * Callback for processing a received Rabbit message.
     * <p>Implementors are supposed to process the given Message,
     * typically sending reply messages through the given Session.
     *
     * @param message the received AMQP message (never <code>null</code>)
     * @param channel the underlying Rabbit Channel (never <code>null</code>)
     * @throws Exception Any.
     */
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        try {
            Calendar calendar = Calendar.getInstance();

            log.info("------------------------周期雷达数据接入------------------------");
            log.info("数据到的时间:" + sdf.format(new Date()));
            log.info("----------------------------------------------------------");

            String orgData = new String(message.getBody());
            MultipleDataPo multipleDataPo = null;
            SingleDataPo singleDataPo = null;
            RadarSitePo radarSitePo = null;
            // 如果收到数据为null，直接跳出；
            if (null != orgData) {
                multipleDataPo = new MultipleDataPo();
                // 根据行结束符进行分割
                String[] rowDatas = orgData.split("\\r\\n");
                if (null != rowDatas && rowDatas.length > 0) {
                    // 解析第一行数据(包含设备编码，开始时间，结束时间，经纬度，数据类型)
                    if (null != rowDatas[0]) {
                        String[] multipleData = rowDatas[0].split(",");
                        if (null != multipleData && multipleData.length == 5) {
                            // 雷达编码
                            multipleDataPo.setRADARCODE(multipleData[0]);
                            radarSitePo = radarService.getRadarSiteInfo(multipleData[0]);
                            multipleDataPo.setSITECODE(radarSitePo.getSiteCode());
                            multipleDataPo.setSITENAME(radarSitePo.getSiteName());
                            multipleDataPo.setSITETYPECODE(radarSitePo.getSiteTypeCode());
                            // 开始时间
                            multipleDataPo.setSTARTTIME(multipleData[1]);
                            // 结束时间
                            multipleDataPo.setENDTIME(multipleData[2]);
                            // 中心点经纬度
                            String[] loc = multipleData[3].split(" ");
                            if (null != loc && loc.length == 2) {
                                double[] locs = new double[2];
                                locs[0] = ConvertUtil.toDouble(loc[0]);
                                locs[1] = ConvertUtil.toDouble(loc[1]);
                                multipleDataPo.setRADARLOC(locs);
                            }
                            // 数据类型（C水平扫描；L定向扫描）
                            multipleDataPo.setDATATYPE(multipleData[4]);

                            int dataLen = 1000;
                            double[] distance = new double[dataLen + 1];

                            // 解析第二行数据（包括 采集角度，采集时间）
                            SingleDataPo[] singleDataPos = null;
                            double[][] pm2_5Data = null;
                            double[][] pm10Data = null;
                            if (null != rowDatas[1]) {
                                String[] angleData = rowDatas[1].split(",");
                                pm2_5Data = new double[angleData.length - 1][dataLen + 1];
                                pm10Data = new double[angleData.length - 1][dataLen + 1];
                                singleDataPos = new SingleDataPo[angleData.length - 1];
                                for (int i = 1; i < angleData.length; i++) {
                                    // 角度po中只用到了ANGLE,MONITORTIME,DISTANCEDATA
                                    singleDataPo = new SingleDataPo();
                                    String[] angle = angleData[i].split(" ");
                                    // 夹角
                                    singleDataPo.setANGLE(ConvertUtil.toInteger(angle[0]));
                                    // 采集时间
                                    singleDataPo.setMONITORTIME(angle[1] + " " + angle[2]);
                                    singleDataPos[i - 1] = singleDataPo;
                                }
                                // 解析距离数据
                                for (int n = 0; n < dataLen + 1; n++) {
                                    double lend = (double) n * radarSitePo.getRadius() / 1000000;
                                    distance[n] = lend;

                                    for (int i = 2, len = rowDatas.length; i < len; i++) {
                                        if (null != rowDatas[i]) {
                                            String[] disData = rowDatas[i].split(",");
                                            if (lend == ConvertUtil.toDouble(disData[0])) {
                                                for (int j = 1; j < disData.length; j++) {
                                                    // 每个点位的pm10,pm25值
                                                    String[] data = disData[j].split("/");
                                                    pm10Data[j - 1][n] = ConvertUtil.toDouble(data[0]);
                                                    pm2_5Data[j - 1][n] = ConvertUtil.toDouble(data[1]);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }

                                for (int i = 1; i < angleData.length; i++) {

                                    byte[] beforePM25 = JSON.toJSONBytes(pm2_5Data[i - 1]);
                                    byte[] beforePM10 = JSON.toJSONBytes(pm10Data[i - 1]);
                                    byte[] afterPM25 = ZLibUtils.compress(beforePM25);
                                    byte[] afterPM10 = ZLibUtils.compress(beforePM10);

                                    singleDataPos[i - 1].setPM2_5(Base64Util.base64(afterPM25));
                                    singleDataPos[i - 1].setPM10(Base64Util.base64(afterPM10));
                                }

                                multipleDataPo.setDISTANCE(distance);
                                multipleDataPo.setANGLEDATA(singleDataPos);
                                multipleDataPo.setCREATETIME(sdf.format(calendar.getTime()));
                                radarService.checkMissingTrapMultipleData(multipleDataPo);

                                if ("C".equals(multipleDataPo.getDATATYPE())) {
                                    for (int i = 1; i < angleData.length; i++) {
                                        singleDataPos[i - 1].setPM2_5(null);
                                        singleDataPos[i - 1].setPM10(null);
                                        singleDataPos[i - 1].setPM2_5S(pm2_5Data[i - 1]);
                                        singleDataPos[i - 1].setPM10S(pm10Data[i - 1]);
                                    }
                                    multipleDataPo.setANGLEDATA(singleDataPos);

                                    // 解析栅格报警字段
                                    List<Map<String, String>> mapList = getAlarmMap(radarSitePo.getLimit());

                                    // 推送周期原始/栅格消息
                                    Map<String, Object> gridResultMap = this.getRadarGridData(multipleDataPo, radarSitePo, mapList);
//                                messageProducer.sendMessageByHistoryExchange("radarShow_" + multipleDataPo.getRADARCODE(), "original", JSON.toJSONString(multipleDataPo));
                                    gridResultMap.put("original", transformationMultipleDataToMap(multipleDataPo));
                                    gridResultMap.put("snCode", multipleDataPo.getRADARCODE());

                                    messageProducer.sendMessage("", "radarGeneratePic", JSON.toJSONString(gridResultMap));
                                }
                                log.info("数据处理完成:" + sdf.format(new Date()));
                                log.info("----------------------------------------------------------");
                                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                                return;

                            }
                        }
                    }
                }
            }
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
        } catch (Exception e) {
            e.printStackTrace();
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
        }
    }

    /**
     * 转换报警阀值
     *
     * @param limit
     * @return
     */

    private List<Map<String, String>> getAlarmMap(String limit) {
        List<Map<String, String>> mapList = new ArrayList<>();
        Map<String, String> alarmMap = null;
        if (!StringUtils.isEmpty(limit)) {
            String[] alarms = limit.split("/");
            if (null != alarms && alarms.length > 0) {
                for (String alarm : alarms) {
                    String[] alarmInfo = alarm.split("##");
                    if (null != alarmInfo && alarmInfo.length > 4) {
                        if (RegexUtil.isDouble(alarmInfo[0]) && RegexUtil.isDouble(alarmInfo[3])) {
                            alarmMap = new HashMap<>();
                            alarmMap.put("grid", alarmInfo[0]);
                            alarmMap.put("factorType", alarmInfo[1]);
                            alarmMap.put("dataType", alarmInfo[2]);
                            alarmMap.put("value", alarmInfo[3]);
                            alarmMap.put("num", alarmInfo[4]);
                            mapList.add(alarmMap);
                        }
                    }
                }
            }
        }
        return mapList;
    }

    /**
     * 周期数据的栅格化
     *
     * @param multipleDataPo
     * @param radarSitePo
     * @param alarmList
     * @return
     */

    private Map<String, Object> getRadarGridData(MultipleDataPo multipleDataPo, RadarSitePo radarSitePo, List<Map<String, String>> alarmList) {
        Map<String, Object> gridResultMap = new HashMap<>();
        Map<String, Object> resultMap = null;
        Map<String, Object> maxMap = null;
        double radius = radarSitePo.getRadius();

        double[] stepStr = {100, 200, 500};
        double pm25Total = 0.0;
        double pm10Total = 0.0;
        double pm25Avg = 0.0;
        double pm10Avg = 0.0;
        Map<String, Object> orgMap = null;
        Object[][] result = null;
        List<Object> tempList = null;
        Map<String, List<Object>> gridMap = null;
        Map<String, String> idsMap = null;
        List<String> pm10_alarm = null;
        List<String> pm25_alarm = null;

        LocationUtil locationUtil = new LocationUtil(multipleDataPo.getRADARLOC()[0], multipleDataPo.getRADARLOC()[1]);
        for (double step : stepStr) {
            resultMap = new HashMap<>();
            idsMap = new HashMap<>();
            pm10_alarm = new ArrayList<>();
            pm25_alarm = new ArrayList<>();
            maxMap = new HashMap<>();
            maxMap.put("pm25Max", "0.0");
            maxMap.put("pm25xy", "");
            maxMap.put("pm10Max", "0.0");
            maxMap.put("pm10xy", "");
            idsMap.put("pm25_" + (int) step, UUID.randomUUID().toString().replace("-", ""));
            idsMap.put("pm10_" + (int) step, UUID.randomUUID().toString().replace("-", ""));
            // 个数
            int lent = (int) (radius * 2 / step);
            result = new Object[lent][lent];

            gridMap = new HashMap<>();
            for (SingleDataPo po : multipleDataPo.getANGLEDATA()) {
                int reAngle = Rasterize.getReXAngle(po.getANGLE());
                for (int n = 0; n < radius / 5 + 1; n++) {
                    double xPonit = Rasterize.getXPoint(n * 5, reAngle);
                    double yPonit = Rasterize.getYPoint(n * 5, reAngle);
                    String grid = Rasterize.getGrid(xPonit, yPonit, radius, step);
                    List<Object> gridDatas = gridMap.get(grid);
                    Map<String, Double> pointData = new HashMap<>();
                    pointData.put("pm25", po.getPM2_5S()[n]);
                    pointData.put("pm10", po.getPM10S()[n]);
                    if (null == gridDatas) {
                        tempList = new ArrayList<>();
                        tempList.add(pointData);
                        gridMap.put(grid, tempList);
                    } else {
                        gridDatas.add(pointData);
                    }

                }
            }
            // 遍历gridMap,获取每个栅格的原始数据
            for (Map.Entry<String, List<Object>> entry : gridMap.entrySet()) {
                pm25Total = 0.0;
                pm10Total = 0.0;
                pm25Avg = 0.0;
                pm10Avg = 0.0;
                String[] xyGrids = entry.getKey().split("##");
                int x = (int) ((radius - Double.valueOf(xyGrids[1])) / step);
                int y = (int) ((radius + Double.valueOf(xyGrids[0])) / step);

                List<Object> dataList = entry.getValue();
                orgMap = new HashMap<>();
                if (dataList != null && dataList.size() > 0) {
                    for (Object obj : dataList) {
                        Map<String, Double> pointData = (Map<String, Double>) obj;
                        pm25Total += pointData.get("pm25");
                        pm10Total += pointData.get("pm10");
                    }
                    orgMap.put("x", x);
                    orgMap.put("y", y);
                    orgMap.put("pm25Total", df2.format(pm25Total));
                    orgMap.put("pm10Total", df2.format(pm10Total));
                    pm25Avg = pm25Total / dataList.size();
                    pm10Avg = pm10Total / dataList.size();
                    if ("Avg".equals(radarSitePo.getMax())) {
                        if (Double.valueOf(maxMap.get("pm25Max").toString()) < pm25Avg) {
                            maxMap.put("pm25Max", df2.format(pm25Avg));
                            maxMap.put("pm25xy", x + "/" + y);
                        }
                        if (Double.valueOf(maxMap.get("pm10Max").toString()) < pm10Avg) {
                            maxMap.put("pm10Max", df2.format(pm10Avg));
                            maxMap.put("pm10xy", x + "/" + y);
                        }
                    } else {
                        if (Double.valueOf(maxMap.get("pm25Max").toString()) < pm25Total) {
                            maxMap.put("pm25Max", df2.format(pm25Total));
                            maxMap.put("pm25xy", x + "/" + y);
                        }
                        if (Double.valueOf(maxMap.get("pm10Max").toString()) < pm10Total) {
                            maxMap.put("pm10Max", df2.format(pm10Total));
                            maxMap.put("pm10xy", x + "/" + y);
                        }
                    }

                    orgMap.put("pm25Avg", df2.format(pm25Avg));
                    orgMap.put("pm10Avg", df2.format(pm10Avg));

                }
                result[x][y] = orgMap;
            }

            // 栅格报警计算，先进行排序，然后去前n值
            for (Map<String, String> alarm : alarmList) {
                if (step != Double.valueOf(alarm.get("grid"))) {
                    continue;
                }
                resultMap.put("limit_" + alarm.get("factorType"), alarm.get("value"));

                List<Map<String, Object>> sortMap = sortGridMap(result, alarm.get("factorType") + alarm.get("dataType"));
                double dataValue = 0.0;
                int numValue = 1;
                int num = Integer.parseInt(alarm.get("num"));
                int data = Integer.parseInt(alarm.get("value"));
                for (Map<String, Object> obj : sortMap) {
                    dataValue = Double.valueOf(obj.get(alarm.get("factorType") + alarm.get("dataType")).toString());
                    if (numValue > num) {
                        break;
                    }
                    if (dataValue < data) {
                        break;
                    }
                    if ("pm25".equals(alarm.get("factorType"))) {
                        pm25_alarm.add(obj.get("x") + "/" + obj.get("y"));
                    } else if ("pm10".equals(alarm.get("factorType"))) {
                        pm10_alarm.add(obj.get("x") + "/" + obj.get("y"));
                    } else {

                    }

                    overLimitAndAlarmInfo(idsMap.get(alarm.get("factorType") + "_" + alarm.get("grid")), dataValue, alarm, multipleDataPo, Integer.parseInt(obj.get("x").toString()), Integer.parseInt(obj.get("y").toString()), step, locationUtil);
                    numValue++;
                }

            }

            resultMap.put("ids", idsMap);
            resultMap.put("max", maxMap);
            resultMap.put("pm25_alarm", pm25_alarm);
            resultMap.put("pm10_alarm", pm10_alarm);
//            resultMap.put("data", result);
//            messageProducer.sendMessageByHistoryExchange("radarShow_" + multipleDataPo.getRADARCODE(), "grid_" + step, JSON.toJSONString(resultMap));
            gridResultMap.put("grid_" + (int) step, resultMap);
        }
        return gridResultMap;
    }

    /**
     * 栅格数据排序
     *
     * @param gridData
     * @param valueStr
     * @return
     */

    private List<Map<String, Object>> sortGridMap(Object[][] gridData, String valueStr) {
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (int i = 0; i < gridData.length; i++) {
            for (int j = 0; j < gridData[i].length; j++) {
                if (null == gridData[i][j]) {
                    continue;
                }
                mapList.add((Map<String, Object>) gridData[i][j]);
            }
        }
        Collections.sort(mapList, (before, after) -> {
            Map<String, Object> beforeMap = before;
            Map<String, Object> afterMap = after;

            if (null == beforeMap.get(valueStr) && null == afterMap.get(valueStr)) {
                return 0;
            } else if (null == beforeMap.get(valueStr) && null != afterMap.get(valueStr)) {
                return 1;
            } else if (null != beforeMap.get(valueStr) && null == afterMap.get(valueStr)) {
                return -1;
            } else {
                Double one = Double.valueOf(beforeMap.get(valueStr).toString()) * 100;
                Double two = Double.valueOf(afterMap.get(valueStr).toString()) * 100;
                return (int) (two - one);
            }
        });

        return mapList;
    }

    /**
     * 栅格的报警和推送报警数据(入库和联动操作)
     *
     * @param orgValue
     * @param alarm
     */

    private void overLimitAndAlarmInfo(String imageUrl, double orgValue, Map<String, String> alarm, MultipleDataPo multipleDataPo, int x, int y, double step, LocationUtil locationUtil) {

        // 报警入库
        Map<String, Object> radarAlarm = new HashMap<>();
        radarAlarm.put("id", UUID.randomUUID().toString().replace("-", ""));
        radarAlarm.put("siteCode", multipleDataPo.getSITECODE());
        radarAlarm.put("siteName", multipleDataPo.getSITENAME());
        radarAlarm.put("snCode", multipleDataPo.getRADARCODE());
        radarAlarm.put("factor", alarm.get("factorType"));
        radarAlarm.put("dataType", alarm.get("dataType"));
        radarAlarm.put("type", (int) step);
        try {
            radarAlarm.put("startTime", sdf.parse(multipleDataPo.getSTARTTIME()));
            radarAlarm.put("endTime", sdf.parse(multipleDataPo.getENDTIME()));
        } catch (Exception e) {
            e.printStackTrace();
        }

        radarAlarm.put("data", orgValue);
        radarAlarm.put("alarmData", Double.valueOf(alarm.get("value")));
        radarAlarm.put("dataLoc", x + "," + y);

        double[][] loc = Rasterize.getPointLoc(x, y, step, locationUtil);
        double gridX = x + step / 2;
        double gridY = y - step / 2;
        radarAlarm.put("loc", this.transformationToString(loc));
        radarAlarm.put("distance", Rasterize.getPonitDistance(gridX, gridY));

        double[] gridLoc = Rasterize.getGridPonitLoc(gridX, gridY, locationUtil);
        radarAlarm.put("gridLoc", gridLoc[0] + "," + gridLoc[1]);
        radarAlarm.put("angle", Rasterize.getReYAngle(gridX, gridY));
        radarAlarm.put("radarPic", imageUrl);
        radarAlarm.put("createDate", new Date());
        radarBaseDao.insertRadarAlarm(radarAlarm);

        // 联动日志
        Map<String, Object> linkageLog = new HashMap<>();
        linkageLog.put("id", UUID.randomUUID().toString().replace("-", ""));
        linkageLog.put("siteCode", multipleDataPo.getSITECODE());
        linkageLog.put("radarSnCode", multipleDataPo.getRADARCODE());
        linkageLog.put("videoSnCode", "");
        linkageLog.put("url", "");
        linkageLog.put("monitorTime", new Date());
        try {
            // 调取联动

            linkageLog.put("type", "0");
            linkageLog.put("result", "");
        } catch (Exception e) {
            linkageLog.put("type", "1");
            linkageLog.put("exception", multipleDataPo.getSITECODE());
            linkageLog.put("exceMessage", multipleDataPo.getSITECODE());
        }

        radarBaseDao.insertLinkageLog(linkageLog);

        // 推送报警消息
        Map<String, Object> sendMap = new HashMap<>();
        sendMap.put("content", multipleDataPo.getSITENAME() + "站点雷达数据在" + multipleDataPo.getSTARTTIME() + "至" + multipleDataPo.getENDTIME() + "时间段，扫描到" + "经纬度" + this.transformationToString(loc) + alarm.get("factorType") + alarm.get("dataType") + "数据超标；当前值为：" + df2.format(orgValue) + ",标准值为：" + alarm.get("value"));
        sendMap.put("siteCode", multipleDataPo.getSITECODE());
        sendMap.put("siteName", multipleDataPo.getSITENAME());
        sendMap.put("matchCase", radarBaseDao.getRadarAlarmCase(multipleDataPo.getRADARCODE()));
        messageProducer.sendMessage("", "sendInformation", JSON.toJSONString(sendMap));


    }

    /**
     * 转换成小写
     *
     * @param multipleDataPo
     * @return
     */

    private Map<String, Object> transformationMultipleDataToMap(MultipleDataPo multipleDataPo) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("siteCode", multipleDataPo.getSITECODE());
        resultMap.put("siteName", multipleDataPo.getSITENAME());
        resultMap.put("radarCode", multipleDataPo.getRADARCODE());
        resultMap.put("endTime", multipleDataPo.getENDTIME());
        resultMap.put("startTime", multipleDataPo.getSTARTTIME());
        resultMap.put("siteName", multipleDataPo.getSITENAME());
        resultMap.put("createTime", multipleDataPo.getCREATETIME());
        resultMap.put("dataType", multipleDataPo.getDATATYPE());
        resultMap.put("siteTypeCode", multipleDataPo.getSITETYPECODE());
        resultMap.put("radarLoc", multipleDataPo.getRADARLOC());
        resultMap.put("distance", "");

        Map<String, Object> singleMap = null;
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (SingleDataPo singleDataPo : multipleDataPo.getANGLEDATA()) {
            singleMap = new HashMap<>();
            singleMap.put("angle", singleDataPo.getANGLE());
            singleMap.put("monitorTime", singleDataPo.getMONITORTIME());
            singleMap.put("pm25s", singleDataPo.getPM2_5S());
            singleMap.put("pm10s", singleDataPo.getPM10S());
            mapList.add(singleMap);
        }

        resultMap.put("angleData", mapList);
        resultMap.put("pm25_image", UUID.randomUUID().toString().replace("-", ""));
        resultMap.put("pm10_image", UUID.randomUUID().toString().replace("-", ""));

        return resultMap;
    }

    /**
     * 转换经纬度
     *
     * @param locs
     * @return
     */

    private String transformationToString(double[][] locs) {
        String result = "";
        if (locs.length == 0) {
            return result;
        }
        for (double[] loc : locs) {
            result += "[" + loc[0] + "," + loc[1] + "]";
        }
        return result;
    }
}
